import { User, Duel, Event } from './duel';


export interface cardData {
    readonly id: symbol;
    name: string;
    desc: string;
    type: string[];
    attribute?: string;
    level?: number;
    attack?: number;
    defend?: number;
    keywords?: string[];
    require?: string[]
}

export interface monsterData extends cardData {
    attribute: string;
    level: number;
    attack: number;
    defend: number;
}




// card mold
export interface Card {
    symbol: Symbol;
    owner: User;
    data: cardData;
    currentData: cardData;
    place?: any;
    state?: boolean;
    front?: boolean;
    coverTime?: number;
    effects?: Effect[];
    lifeCycle?: LifeCycle;
}

export interface Effect {
    text: string,
    effect: Function,
    condition: object
}

export class Card {
    events = {};
    globalEvent = new Map();
    constructor(cardData: cardData, owner: User, effects: Effect[] = [], lifeCycle: object = {}) {
        this.symbol = Symbol();
        this.owner = owner;
        this.effects = effects;
        this.lifeCycle = lifeCycle;

        let data = cardData;
        Object.defineProperty(this, 'data', {
            get() {
                if (this.front || this.isMyCard) {
                    return data;
                } else {
                    return null;
                }
            },
            set() {}
        });

        let currentData = {...cardData};
        Object.defineProperty(this, 'currentData', {
            get() {
                if (this.front || this.isMyCard) {
                    return currentData;
                } else {
                    return null;
                }
            }
        });
    }    
    loopEvents(eventName: string) {
        if (this.events[eventName]) {
            for (let event of this.events[eventName]) { event() }
        }
    }
    _effect_(index: number = 0, ...args: any) {
        this.front = true;
        this.owner.speak(this.currentData.name + ' 的效果发动，' + this.effects[index].text);
        this.effects[index].effect.call(this, ...args);
    }
    available(index: number, type: string) {
        if (this.effects[index].condition.hasOwnProperty(type)) {
            return this.effects[index].condition[type].call(this);
        } else {
            return false;
        }
    }
    createEvent(eventName: string, callback: Function) {
        callback = callback.bind(this);
        
        return {
            subscriber: this,
            name: eventName,
            callback
        }
    }
    registEvent(event: Event) {
        let symbol = this.owner.duel.addEvent(event);

        if (!this.globalEvent.has(event.name)) { this.globalEvent.set(event.name, new Set()) }

        this.globalEvent.get(event.name).add(symbol);
    }
    removeEvent(eventName: string, symbol: Symbol) {
        if (!this.globalEvent.has(eventName)) {
            return;
        }

        if (symbol) {
            this.globalEvent.get(eventName).delete(symbol);
            this.owner.duel.delEvent(eventName, symbol);
        } else {
            for (let symbol of this.globalEvent.get(eventName).values()) {
                this.owner.duel.delEvent(eventName, symbol);                
            }
            this.globalEvent.delete(eventName);
        }
    }
    get isMyCard(): Boolean {
        return this.owner === this.owner.duel.me;
    }
}




// monster card
export class Monster extends Card {
    constructor(monsterData: cardData, owner: User, effects: Effect[] = [], lifeCycle: object = {}) {
        super(monsterData, owner, effects, lifeCycle);
        this.front = true;
    }
    // change ATK/DEF
    addATK(number: number) {
        this.currentData.attack += number;
    }
    addDEF(number: number) {
        this.currentData.defend += number;
    }
    attack(target: Monster) {
        this.owner.attack(this, target);
    }
    // broken
    battleBroken() {}
    summon() {
        this.place = this.owner.filed;
        this.state = true;
        this.front = true;

        if (this.lifeCycle.summon) {
            this.lifeCycle.summon.call(this);
        }
    }
    specialSummon() {
        this.place = this.owner.filed;
        this.state = true;
        this.front = true;

        if (this.lifeCycle.specialSummon) {
            this.lifeCycle.specialSummon.call(this);
        }
    }
}




// magic card
export class Magic extends Card {
    constructor(cardData: cardData, owner: User, effects: Effect[] = [], lifeCycle: object = {}) {
        super(cardData, owner, effects, lifeCycle);
    }
}




// tragic card
export class Tragic extends Card {
    constructor(cardData: cardData, owner: User, effects: Effect[] = [], lifeCycle: object = {}) {
        super(cardData, owner, effects, lifeCycle);
    }
    effect(...args: any) {
        if (this.coverTime >= 1) {
            this._effect_(...args);
        }
    }
}




export interface Desk {
    owner: User;
    cards: Card[]
}

export interface cardMold {
    data: cardData;
    effects?: Effect[];
    lifeCycle?: LifeCycle
}

export interface LifeCycle {    
    // 生命周期
    draw?: Function;
    toHand?: Function;
    summon?: Function;
    specialSummon?: Function;
    broken?: Function;
    battleBroken?: Function;
    effectBroken?: Function;
    toCemetery?: Function;
    outOfGame?: Function;
    leaveFiled?: Function;
}

export class Desk {
    constructor(owner: User, cards?:object[]) {
        this.cards = [];
        this.owner = owner;

        if (cards) { this.load(cards) }
    }
    load(cards: object[]) {
        cards.forEach(({ data, lifeCycle = {}, effects = [] }: cardMold) => {
            let tmp;
            if (data.type[0] === 'monster') {
                tmp = new Monster(data, this.owner, effects, lifeCycle);
            } else if (data.type[0] === 'magic') {
                tmp = new Magic(data, this.owner, effects, lifeCycle);
            } else if (data.type[0] === 'tragic') {
                tmp = new Tragic(data, this.owner, effects, lifeCycle);
            }
            this.cards.push(tmp);
        })
    }
    draw(number: number = 1) {
        if (number > this.cards.length) {
            return [];
        }

        let res = [];
        for (let i = 0; i < number; i++) {
            res.push(this.cards.pop())
        }

        return res;
    }
    shuffle() {

    }
    filter(filter: Function) {
        return this.cards.filter(item => filter(item));
    }
}